﻿unit FTPClientContext;

interface

uses
  Winapi.Windows, System.Classes, System.SysUtils, Protocol, CircularBuffer, MemBuffer,
  ContextFactory, FTPProctol, FTPSocketSend, EventHandler, Engine, DataPacketSplit, TBCommon;

type

  //在任何网络出错的时候，需要关闭CloseFile
  TFTPClientContext = class(TIClientContext)
  private
    m_bLogin:                   Boolean;                          //是否登录
    m_FtpSocketSend:            TFTPSocketSend;                   //发送打包类
    m_RecvCircularBuffer:       TCircularBuffer;                  //接收环形Buffer区(打包)
    //上传
    m_dUpFilePos:               Int64;                            //文件起始点
    m_dUpFileSize:              Int64;                            //文件长度
    m_dUpBlockSize:             Cardinal;                            //块大小
    m_dUpEndBlockSize:          Cardinal;                            //最后一块大小
    m_dUpBlockCount:            Int64;                            //传输块数
    m_dUpTransIndex:            Int64;                            //传输块索引(下一次)
    m_dUpEncryptKey:            WORD;                             //密钥
    m_UpFileStream:             TFileStream;                      //文件
    //下载
    m_dDownFilePos:             int64;
    m_dDownFileIndex:           Cardinal;                            //文件索引
    m_dDownFileSize:            Int64;                            //文件长度
    m_dDownBlockSize:           Cardinal;                            //块大小
    m_dDownEndBlockSize:        Cardinal;                            //最后一块大小
    m_dDownBlockCount:          Int64;                            //传输块数
    m_dDownTransIndex:          Int64;                            //传输块索引(下一次)
    m_dDownEncryptKey:          WORD;                             //密钥
    m_DownFileStream:           TFileStream;                      //文件
    m_pUpBlockData:   array[0..Glo_UploadBlockData-1] of Byte;    //块打包缓冲
    m_pDownBlockData: array[0..Glo_DownLoadBlockData-1] of Byte;  //块打包缓冲
  published
    property FRecvCircularBuffer: TCircularBuffer read m_RecvCircularBuffer write m_RecvCircularBuffer;
  private
    procedure InitUpLoad;                                         //初始始上传
    procedure InitDownLoad;                                       //初始始下载
    procedure CreateEncryptKey;                                   //生成密钥
    //拆包信息
    function OnSM_FTP_LOGIN:                    FN_HANDLE_STATUS;
    function OnSM_FTP_UPLOAD_REQUEST:           FN_HANDLE_STATUS;
    function OnSM_FTP_UPLOAD_CHECK:             FN_HANDLE_STATUS;
    function OnSM_FTP_UPLOAD_DATA:              FN_HANDLE_STATUS;
    function OnSM_FTP_UPLOAD_OVER:              FN_HANDLE_STATUS;
    function OnSM_FTP_DOWNLOAD_REQUEST:         FN_HANDLE_STATUS;
    function OnSM_FTP_DOWNLOAD_DATA:            FN_HANDLE_STATUS;
    function OnSM_FTP_DOWNLOAD_OVER:            FN_HANDLE_STATUS;
    function OnSM_FTP_DOWNLOAD_FILELIST:        FN_HANDLE_STATUS;
    function OnSM_FTP_DOWNLOAD_FILELIST_NEXT:   FN_HANDLE_STATUS;
  public
    procedure ProcessDataStream(FBuffer: TIBuffer); override;
  public
    procedure InitClientContext; override;
    procedure ClearSession;
    constructor Create(FEngine: TIEngine);
    destructor Destroy; override;
  end;

implementation

uses crc32, DataPacketSend, FTPDirectory, FTPEngine;

constructor TFTPClientContext.Create(FEngine: TIEngine);
begin
  inherited Create(FEngine);
  CreateEncryptKey;
  m_dUpFilePos := 0;
  m_dUpFileSize := 0;
  m_dUpBlockSize := 0;
  m_dUpBlockCount := 0;
  m_dUpTransIndex := 1;
  m_dUpEncryptKey := 0;
  m_dDownFileSize := 0;
  m_dDownBlockSize := 0;
  m_dDownEndBlockSize := 0;
  m_dDownBlockCount := 0;
  m_dDownTransIndex := 1;
  m_dDownEncryptKey := 0;
  m_FtpSocketSend := TFTPSocketSend.Create(FEngine);
  m_FtpSocketSend.FDIDataPacketSend.OnSendBufToQueue := SendStream;
  m_RecvCircularBuffer := TCircularBuffer.Create(RECV_CIRCULAR_BUFFER_SIZE);
  m_RecvCircularBuffer.SetEmpty;
  //注册事件
  RegisterEvent( SM_FTP_LOGIN,                  'OnSM_FTP_LOGIN',                   OnSM_FTP_LOGIN);
  RegisterEvent( SM_FTP_UPLOAD_REQUEST,         'OnSM_FTP_UPLOAD_REQUEST',          OnSM_FTP_UPLOAD_REQUEST);
  RegisterEvent( SM_FTP_UPLOAD_CHECK,           'OnSM_FTP_UPLOAD_CHECK',            OnSM_FTP_UPLOAD_CHECK);
  RegisterEvent( SM_FTP_UPLOAD_DATA,            'OnSM_FTP_UPLOAD_DATA',             OnSM_FTP_UPLOAD_DATA);
  RegisterEvent( SM_FTP_UPLOAD_OVER,            'OnSM_FTP_UPLOAD_OVER',             OnSM_FTP_UPLOAD_OVER);
  RegisterEvent( SM_FTP_DOWNLOAD_REQUEST,       'OnSM_FTP_DOWNLOAD_REQUEST',        OnSM_FTP_DOWNLOAD_REQUEST);
  RegisterEvent( SM_FTP_DOWNLOAD_DATA,          'OnSM_FTP_DOWNLOAD_DATA',           OnSM_FTP_DOWNLOAD_DATA);
  RegisterEvent( SM_FTP_DOWNLOAD_OVER,          'OnSM_FTP_DOWNLOAD_OVER',           OnSM_FTP_DOWNLOAD_OVER);
  RegisterEvent( SM_FTP_DOWNLOAD_FILELIST,      'OnSM_FTP_DOWNLOAD_FILELIST',       OnSM_FTP_DOWNLOAD_FILELIST);
  RegisterEvent( SM_FTP_DOWNLOAD_FILELIST_NEXT, 'OnSM_FTP_DOWNLOAD_FILELIST_NEXT',  OnSM_FTP_DOWNLOAD_FILELIST_NEXT);
end;

destructor TFTPClientContext.Destroy;
begin
  if Assigned(m_UpFileStream) then FreeAndNil(m_UpFileStream);
  FreeAndNil(m_FtpSocketSend);
  FreeAndNil(m_RecvCircularBuffer);
  inherited Destroy;
end;

procedure TFTPClientContext.InitClientContext;
begin
  inherited InitClientContext;
  if Assigned(m_UpFileStream) then FreeAndNil(m_UpFileStream);
  m_RecvCircularBuffer.SetEmpty;
end;

procedure TFTPClientContext.ClearSession;
begin
  //网络断开时, 初始化清理工作
  InitUpLoad;
  InitDownLoad;
end;

procedure TFTPClientContext.CreateEncryptKey;
begin
  Randomize;
  m_dUpEncryptKey := Random(65535);
  m_dDownEncryptKey := Random(65535);
end;

procedure TFTPClientContext.InitUpLoad;
begin
  m_dUpFilePos := 0;
  m_dUpFileSize := 0;
  m_dUpBlockSize := 0;
  m_dUpBlockCount := 0;
  m_dUpTransIndex := 1;
  m_dUpEncryptKey := 0;
  if Assigned(m_UpFileStream) then FreeAndNil(m_UpFileStream);
end;

procedure TFTPClientContext.InitDownLoad;
begin
  m_dDownFileSize := 0;
  m_dDownBlockSize := 0;
  m_dDownEndBlockSize := 0;
  m_dDownBlockCount := 0;
  m_dDownTransIndex := 1;
  m_dDownEncryptKey := 0;
  if Assigned(m_DownFileStream) then FreeAndNil(m_DownFileStream);
end;

procedure TFTPClientContext.ProcessDataStream(FBuffer: TIBuffer);
begin
  if not Assigned(FBuffer) then Exit;
  //数据包加入环形缓冲区
  FRecvCircularBuffer.WriteCirBuffer(PChar(FBuffer.GetBuffer), FBuffer.GetUsed);
  //拼包处理
  _DIDataPacketSplit.ProcessSplitDataPack(Self, FRecvCircularBuffer);
end;

function TFTPClientContext.OnSM_FTP_LOGIN: FN_HANDLE_STATUS;
var
  DIFTPPak_CSUserLogin: TDIFTPPak_CSUserLogin;
begin
  ZeroMemory(@DIFTPPak_CSUserLogin, SizeOf(TDIFTPPak_CSUserLogin));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_CSUserLogin, SizeOf(TDIFTPPak_CSUserLogin) );
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_LOGIN退出, 长度不够']);
    Exit;
  end;
  //生成事件包
  if Result = FN_HANDLE_SUCCESS then
  begin
    ;
  end;
end;

function TFTPClientContext.OnSM_FTP_UPLOAD_REQUEST: FN_HANDLE_STATUS;
var
  sPath: String;
  sCheckSum: Cardinal;
  MemoryStream: TMemoryStream;
  DIFTPPak_CSUpload_Request: TDIFTPPak_CSUpload_Request;         //收到客户端
begin
  ZeroMemory(@DIFTPPak_CSUpload_Request, SizeOf(TDIFTPPak_CSUpload_Request));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self,
    FRecvCircularBuffer, @DIFTPPak_CSUpload_Request, SizeOf(TDIFTPPak_CSUpload_Request));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_UPLOAD_REQUEST退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    sPath := (Self.Engine as TIFTPEngine).Directory.FUpLoadPath + DIFTPPak_CSUpload_Request.sFileName;
    if IsFileInUse(sPath) then
      begin
        InitUpLoad;
        m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail(CM_FTP_UPLOAD_REQUEST, '您上传的文件正在被他人操作!');
        Exit;
      end
    else if FileExists(sPath) then
      begin
        m_UpFileStream := TFileStream.Create(sPath, fmOpenReadWrite);
        m_UpFileStream.Seek(0, soFromBeginning);
        MemoryStream := TMemoryStream.Create;
        try
          MemoryStream.CopyFrom(m_UpFileStream, m_UpFileStream.Size);
          MemoryStream.Seek(0, soFromBeginning);
          sCheckSum := 0;
          CalcCRC32(MemoryStream.Memory, m_UpFileStream.Size, sCheckSum);
          m_FtpSocketSend.Send_CM_FTP_UPLOAD_CHECK(m_UpFileStream.Size, sCheckSum);
        finally
          MemoryStream.Free;
        end;
      end
    else
      begin
        //根据文件大小，计算需要传输的块, 上传文件块
        m_dUpBlockSize := Glo_UploadBlockData;
        m_dUpFileSize := DIFTPPak_CSUpload_Request.dFileSize;
        m_dUpEndBlockSize := m_dUpFileSize mod m_dUpBlockSize;
        m_dUpBlockCount :=  m_dUpFileSize div m_dUpBlockSize;
        if (m_dUpEndBlockSize <> 0) then
          m_dUpBlockCount := m_dUpBlockCount + 1
        else
          m_dUpEndBlockSize := m_dUpBlockSize;
        m_dUpTransIndex := 1;
        //写文件
        m_UpFileStream := TFileStream.Create(sPath, fmCreate);
        m_UpFileStream.Seek(0, soFromBeginning);
        //发送开始包
        m_FtpSocketSend.Send_CM_FTP_UPLOAD_REQUEST(0, 0, m_dUpBlockSize, m_dUpEndBlockSize,
          m_dUpBlockCount, m_dUpTransIndex, m_dUpEncryptKey);
      end;
  end;
end;

function TFTPClientContext.OnSM_FTP_UPLOAD_CHECK: FN_HANDLE_STATUS;
var
  FDIFTPPak_FileCheck_Request: TDIFTPPak_FileCheck_Request ;
begin
  ZeroMemory(@FDIFTPPak_FileCheck_Request, SizeOf(TDIFTPPak_FileCheck_Request));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self,
    FRecvCircularBuffer, @FDIFTPPak_FileCheck_Request, SizeOf(TDIFTPPak_FileCheck_Request));
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    //根据文件大小，计算需要传输的块, 上传文件块
    m_dUpFilePos := FDIFTPPak_FileCheck_Request.dFilePos;
    m_dUpBlockSize := Glo_UploadBlockData;
    m_dUpFileSize := FDIFTPPak_FileCheck_Request.dFileSize - m_dUpFilePos;
    m_dUpEndBlockSize := m_dUpFileSize mod m_dUpBlockSize;
    m_dUpBlockCount :=  m_dUpFileSize div m_dUpBlockSize;
    if (m_dUpEndBlockSize <> 0) then
      m_dUpBlockCount := m_dUpBlockCount + 1
    else
      m_dUpEndBlockSize := m_dUpBlockSize;
    m_dUpTransIndex := 1;
    //写文件
    m_UpFileStream.Seek(FDIFTPPak_FileCheck_Request.dFilePos, soFromBeginning);
    //发送开始包
    m_FtpSocketSend.Send_CM_FTP_UPLOAD_REQUEST(m_dUpFilePos,
      0, m_dUpBlockSize, m_dUpEndBlockSize, m_dUpBlockCount, m_dUpTransIndex, m_dUpEncryptKey);
  end;
end;

function TFTPClientContext.OnSM_FTP_UPLOAD_DATA: FN_HANDLE_STATUS;
var
  m_dRecvSize: Cardinal;
  m_dCheckSum: Cardinal;
  DIFTPPak_TransData_Response: TDIFTPPak_TransData_Response;          //收到客户端
begin
  m_dRecvSize := m_dUpBlockSize;
  if m_dUpTransIndex = m_dUpBlockCount then m_dRecvSize := m_dUpEndBlockSize;
  ZeroMemory(@DIFTPPak_TransData_Response, SizeOf(TDIFTPPak_TransData_Response));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_TransData_Response, SizeOf(TDIFTPPak_TransData_Response),
    @m_pUpBlockData[0], m_dRecvSize);
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_UPLOAD_DATA退出, 长度不够']);
    Exit;
  end;
  //验证，写文件
  if Result = FN_HANDLE_SUCCESS then
  begin
    if (m_dUpEncryptKey = DIFTPPak_TransData_Response.dEncryptKey) then
    begin
      if (DIFTPPak_TransData_Response.dDataBlockID>0) and
         (DIFTPPak_TransData_Response.dDataBlockID <= m_dUpBlockCount) and
         (DIFTPPak_TransData_Response.dDataBlockID = m_dUpTransIndex) then
      begin
        //增加一次最后一个包大小的判断
        if DIFTPPak_TransData_Response.dDataBlockID = m_dUpBlockCount then
          if DIFTPPak_TransData_Response.dBlockSize <> m_dUpEndBlockSize then
          begin
            //关闭文件
            InitUpLoad;
            m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail(CM_FTP_UPLOAD_DATA, '文件最后一块尾大小错误');
            //_FtpServer.AppendLogMessage('%s', ['文件最后一块尾大小错误']);
            Exit;
          end;
        //计算校验和
        m_dCheckSum := 0;
        CalcCRC32(@m_pUpBlockData[0], DIFTPPak_TransData_Response.dBlockSize, m_dCheckSum);
        if DIFTPPak_TransData_Response.dCheckSum <> m_dCheckSum then
        begin
          //关闭文件
            InitUpLoad;
            m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail(CM_FTP_UPLOAD_DATA, '文件校验和出错');
            //_FtpServer.AppendLogMessage('服务器收到校验和: %d, 计算校验和: %d. ',
            //[DIFTPPak_TransData_Response.dCheckSum, m_dCheckSum]);
            Exit;
        end;
        //定位
        m_UpFileStream.Seek(m_dUpFilePos + (m_dUpTransIndex - 1) * m_dUpBlockSize, soFromBeginning);
        m_UpFileStream.WriteBuffer(m_pUpBlockData, DIFTPPak_TransData_Response.dBlockSize);
        //修改下次的索引
        m_dUpTransIndex := m_dUpTransIndex + 1;
        m_FtpSocketSend.Send_CM_FTP_UPLOAD_DATA(m_dUpTransIndex, m_dUpEncryptKey);
      end
      else
      begin
        //关闭文件
        InitUpLoad;
        m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail(CM_FTP_UPLOAD_DATA, '发送文件的块号不对');
//      _FtpServer.AppendLogMessage('块号不对, 服务器m_dUpTransIndex号: %d, 客户端块号: %d.',
//        [m_dUpTransIndex,DIFTPPak_TransData_Response.dDataBlockID ]);
        Exit;
      end;
    end
    else
    begin
      //关闭文件
      InitUpLoad;
      m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail(CM_FTP_UPLOAD_DATA, '密钥验证出错');
//    _FtpServer.AppendLogMessage('第%d次密钥验证出错, S密钥: %d, C密钥: %d',
//     [m_dUpTransIndex, m_dUpEncryptKey, DIFTPPak_TransData_Response.dEncryptKey]);
      Exit;
    end;
  end;
end;

function TFTPClientContext.OnSM_FTP_UPLOAD_OVER: FN_HANDLE_STATUS;
var
  DIFTPPak_CSUpload_OVER: TDIFTPPak_CSUpload_OVER;                  //收到客户端
begin
  ZeroMemory(@DIFTPPak_CSUpload_OVER, SizeOf(TDIFTPPak_CSUpload_OVER));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_CSUpload_OVER, SizeOf(TDIFTPPak_CSUpload_OVER));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_UPLOAD_OVER退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    //_FtpServer.AppendLogMessage('%s', ['服务器收到OnSM_FTP_UPLOAD_OVER消息，关闭文件']);
    //关闭文件
    InitUpLoad;
    m_FtpSocketSend.Send_CM_FTP_UPLOAD_OVER;
  end;
end;

function TFTPClientContext.OnSM_FTP_DOWNLOAD_REQUEST: FN_HANDLE_STATUS;
var
  dBlockData: PByte;
  dCheckSum: Cardinal;
  pFileNode: PTFileNodeItem;
  DIFTPPak_CSDownload_Request: TDIFTPPak_CSDownload_Request;
begin
  ZeroMemory(@DIFTPPak_CSDownload_Request, SizeOf(TDIFTPPak_CSDownload_Request));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_CSDownload_Request, SizeOf(TDIFTPPak_CSDownload_Request));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_DOWNLOAD_REQUEST退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    pFileNode := (Self.Engine as TIFTPEngine).Directory.GetDownLoadFileInfo(DIFTPPak_CSDownload_Request.dFileIndex);
    //打开文件
    m_DownFileStream := TFileStream.Create((Self.Engine as TIFTPEngine).Directory.FDownLoadPath + pFileNode^.sFileName, fmOpenRead or fmShareDenyNone);
    m_DownFileStream.Seek(0, soFromBeginning);
    m_dDownFilePos := DIFTPPak_CSDownload_Request.dFileSize;
    if (m_dDownFilePos > 0) and (m_dDownFilePos <= m_DownFileStream.Size) then
    begin
      GetMem(dBlockData, m_dDownFilePos);
      try
        ZeroMemory(dBlockData, m_dDownFilePos);
        m_DownFileStream.ReadBuffer(dBlockData^, m_dDownFilePos);
        dCheckSum := 0;
        CalcCRC32(@dBlockData^, m_dDownFilePos, dCheckSum);
        if dCheckSum = DIFTPPak_CSDownload_Request.dCheckSum then
        begin
          if m_dDownFilePos = m_DownFileStream.Size then
          begin
            m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_OVER;
            Exit;
          end;
        end;
      finally
        FreeMem(dBlockData, m_dDownFilePos);
      end;
    end;
    //根据文件大小，计算需要传输的块, 上传文件块
    m_dDownBlockSize := Glo_DownLoadBlockData;
    m_dDownFileSize := pFileNode^.sFileSize - m_dDownFilePos;
    m_dDownEndBlockSize := m_dDownFileSize mod m_dDownBlockSize;
    m_dDownBlockCount := m_dDownFileSize div m_dDownBlockSize;
    if (m_dDownEndBlockSize <> 0) then
      m_dDownBlockCount := m_dDownBlockCount +1
    else
      m_dDownEndBlockSize := m_dDownBlockSize;
    m_dDownTransIndex := 1;
//    _FtpServer.AppendLogMessage('服务器下载文件大小: %d, 划分块数: %d, 块大小: %d, 尾块大小: %d.',
//                                [m_dDownFileSize, m_dDownBlockCount, m_dDownBlockSize, m_dDownEndBlockSize]);
    //发送开始包
    m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_REQUEST(m_dDownFilePos, m_dDownFileIndex, m_dDownBlockSize,
      m_dDownEndBlockSize, m_dDownBlockCount, m_dDownTransIndex, m_dDownEncryptKey);
  end;
end;

function TFTPClientContext.OnSM_FTP_DOWNLOAD_DATA: FN_HANDLE_STATUS;
var
  dCheckSum: Cardinal;
  dSendBlockSize: Cardinal;
  DIFTPPak_TransData_Request: TDIFTPPak_TransData_Request;
begin
  ZeroMemory(@DIFTPPak_TransData_Request, SizeOf(TDIFTPPak_TransData_Request));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_TransData_Request, SizeOf(TDIFTPPak_TransData_Request));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_DOWNLOAD_DATA退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    if m_dDownFileIndex <> DIFTPPak_TransData_Request.dFileIndex then
    begin
      //关闭
      InitDownLoad;
      //日志 发送错误信息
//      _FtpServer.AppendLogMessage('服务器文件索引出错, 服务器文件索引: %d, 客户端文件索引: %d.',
//                                 [m_dDownFileIndex, DIFTPPak_TransData_Request.dFileIndex]);
      m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail( CM_FTP_DOWNLOAD_DATA, '文件索引出错');
      Exit;
    end;
    if m_dDownEncryptKey <> DIFTPPak_TransData_Request.dEncryptKey then
    begin
      //关闭
      InitDownLoad;
      //日志 发送错误信息
      m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail( CM_FTP_DOWNLOAD_DATA, '密码出错');
//      _FtpServer.AppendLogMessage('服务器密码验证出错, 服务器密码验证: %d, 客户端密码验证: %d.',
//                                 [m_dDownEncryptKey, DIFTPPak_TransData_Request.dEncryptKey ]);
      Exit;
    end;
    //第一次写0块，所以请求次数要加1
    if DIFTPPak_TransData_Request.dDataBlockID <= m_dDownBlockCount then
    begin
      if (m_dDownTransIndex <> DIFTPPak_TransData_Request.dDataBlockID) then
      begin
        //关闭
        InitDownLoad;
        //日志 发送错误信息
        m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail( CM_FTP_DOWNLOAD_DATA, '文件传输块出错');
//        _FtpServer.AppendLogMessage('服务器文件传输块出错, 服务器传输块: %d, 客户端传输块: %d.',
//                                   [m_dDownTransIndex, DIFTPPak_TransData_Request.dDataBlockID]);
        Exit;
      end;
      //本次发送的文件大小
      dSendBlockSize := m_dDownBlockSize;
      if m_dDownTransIndex = m_dDownBlockCount then dSendBlockSize := m_dDownEndBlockSize;
      ZeroMemory(@m_pDownBlockData[0], Glo_DownLoadBlockData);
      {
      _FtpServer.AppendLogMessage('服务器读块号: %d, 块大小: %d, 读入大小: %d.',
                                 [m_dDownTransIndex, m_dDownBlockSize, dSendBlockSize]);
      }
      //定位
      m_DownFileStream.Seek(m_dDownFilePos + (m_dDownTransIndex - 1)*m_dDownBlockSize, soFromBeginning);
      m_DownFileStream.ReadBuffer(m_pDownBlockData, dSendBlockSize);
      dCheckSum := 0;
      CalcCRC32(@m_pDownBlockData[0], dSendBlockSize, dCheckSum);
      //设置参数
      m_dDownEncryptKey := m_dDownEncryptKey;
      m_dDownTransIndex := m_dDownTransIndex + 1;
      m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_DATA(m_dDownEncryptKey, m_dDownFileIndex,
        m_dDownTransIndex, dSendBlockSize, dCheckSum, @m_pDownBlockData[0]);
    end
    else
    begin
      InitDownLoad;
      m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_OVER;
    end;
  end;
end;

function TFTPClientContext.OnSM_FTP_DOWNLOAD_OVER: FN_HANDLE_STATUS;
var
  DIFTPPak_CSDownload_OVER: TDIFTPPak_CSDownload_OVER;
begin
  ZeroMemory(@DIFTPPak_CSDownload_OVER, SizeOf(TDIFTPPak_CSDownload_OVER));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_CSDownload_OVER, SizeOf(TDIFTPPak_CSDownload_OVER));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
//    _FtpServer.AppendLogMessage('%s', ['OnSM_FTP_DOWNLOAD_OVER退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    InitDownLoad;
    //日志
    m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_OVER;
  end;
end;

function TFTPClientContext.OnSM_FTP_DOWNLOAD_FILELIST: FN_HANDLE_STATUS;
var
  DIFTPPak_CSUserLogin: TDIFTPPak_CSUserLogin;
begin
  ZeroMemory(@DIFTPPak_CSUserLogin, SizeOf(TDIFTPPak_CSUserLogin));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self, FRecvCircularBuffer,
    @DIFTPPak_CSUserLogin, SizeOf(TDIFTPPak_CSUserLogin) );
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_FILELIST退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
    m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_FILELIST((Self.Engine as TIFTPEngine).Directory.GetDownLoadFileCount);
end;


function TFTPClientContext.OnSM_FTP_DOWNLOAD_FILELIST_NEXT: FN_HANDLE_STATUS;
var
  DIFTPPak_SCFileList_Next: TDIFTPPak_SCFileList_Next;
begin
  ZeroMemory(@DIFTPPak_SCFileList_Next, SizeOf(TDIFTPPak_SCFileList_Next));
  Result := _DIDataPacketSplit.SplitDP_ServerGetDataPack(Self,FRecvCircularBuffer,
    @DIFTPPak_SCFileList_Next, SizeOf(TDIFTPPak_SCFileList_Next));
  //包长度不够(下次继续)
  if Result = FN_HANDLE_SPLIT_DATAPACK  then
  begin
    //_FtpServer.AppendLogMessage('%s', ['OnSM_FTP_DOWNLOAD_FILELIST_NEXT退出, 长度不够']);
    Exit;
  end;
  if Result = FN_HANDLE_SUCCESS then
  begin
    if (DIFTPPak_SCFileList_Next.dFileIndex < (Self.Engine as TIFTPEngine).Directory.GetDownLoadFileCount) then
      m_FtpSocketSend.Send_CM_FTP_DOWNLOAD_FILELIST_NEXT( DIFTPPak_SCFileList_Next.dFileIndex,
              (Self.Engine as TIFTPEngine).Directory.GetDownLoadFileInfo(DIFTPPak_SCFileList_Next.dFileIndex).sFileSize,
              @(Self.Engine as TIFTPEngine).Directory.GetDownLoadFileInfo(DIFTPPak_SCFileList_Next.dFileIndex).sFileName[0] )
    else
      m_FtpSocketSend.Send_CM_GLOBAL_RESPONSIONFail( CM_FTP_DOWNLOAD_FILELIST_NEXT,
                                                     '该文件不存在');
  end;
end;

end.
